
#include <sched.h>
#include <stdint.h>
#include <stdlib.h>

#include "plugin_render.h"
#include "plugin_common.h"
#include "plugin_manager.h"
#include "rawfile/raw_file_manager.h"

#ifdef __cplusplus
extern "C" {
#endif

std::unordered_map<std::string, PluginRender *> PluginRender::instance_;

OH_NativeXComponent_Callback PluginRender::callback_;

// 表面创建CB
void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
    LOGE("[Zcy log] OnSurfaceCreatedCB");
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    render->OnSurfaceCreated(component, window);
}

// 表面更改CB
void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    render->OnSurfaceChanged(component, window);
}

// 在表面销毁DB
void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    render->OnSurfaceDestroyed(component, window);
}

// 调度触摸事件
void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    render->DispatchTouchEvent(component, window);
}

PluginRender::PluginRender(std::string &id) : id_(id), component_(nullptr) {
    LOGE("[Zcy log] PluginRender::PluginRender");
    eglCore_ = new EGLCore(id);
    auto renderCallback = PluginRender::GetNXComponentCallback();
    renderCallback->OnSurfaceCreated = OnSurfaceCreatedCB;
    renderCallback->OnSurfaceChanged = OnSurfaceChangedCB;
    renderCallback->OnSurfaceDestroyed = OnSurfaceDestroyedCB;
    renderCallback->DispatchTouchEvent = DispatchTouchEventCB;
}

PluginRender *PluginRender::GetInstance(std::string &id) {
    if (instance_.find(id) == instance_.end()) {
        PluginRender *instance = new PluginRender(id);
        instance_[id] = instance;
        return instance;
    } else {
        return instance_[id];
    }
}

// 获取XComponent回调
OH_NativeXComponent_Callback *PluginRender::GetNXComponentCallback() {
    return &PluginRender::callback_;
}

// 设置本地XComponent
void PluginRender::SetNativeXComponent(OH_NativeXComponent *component) {
    LOGE("[Zcy log] PluginRender::SetNativeXComponent");
    component_ = component;
    OH_NativeXComponent_RegisterCallback(component_, &PluginRender::callback_);
}

int id_size = OH_XCOMPONENT_ID_LEN_MAX + 1;

// 创建完试图后
void PluginRender::OnSurfaceCreated(OH_NativeXComponent *component, void *window) {
    LOGE("[Zcy log] PluginRender::OnSurfaceCreated");
    int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
    LOGE("[Zcy log] PluginRender::OnSurfaceCreated, width=%{public}llu, height=%{public}llu", width_, height_);
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        LOGE("[Zcy log] PluginRender::OnSurfaceCreated eglCore_->GLContextInit");
        //        eglCore_->GLContextInit(window, width_, height_);

        // 多线程
        LOGI("zbclog new pthread");
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
        void *status;
        struct thread_data td;
        td.w = width_;
        td.h = height_;
        td.id = idStr;
        td.window = window;
        td.eglCore = eglCore_;
        LOGI("Zcy w %{public}d h %{public}d", td.w, td.h);
        LOGI("Zcy myids 111 idStr = %{public}s, this->eglCore_->id_ = %{public}s", idStr, this->eglCore_->id_.c_str());
        pthread_create(&tid_, NULL, &EGLCore::start_render, (void *)&td);
        LOGI("zbclog new pthread end");
        pthread_attr_destroy(&attr);
        pthread_detach(tid_);
        /*int rc = pthread_join(tid_, &status);
        if (rc) {
            LOGI("Error:unable to join %{public}d", rc);
        }*/
    }
}

// 选择完视图后
void PluginRender::OnSurfaceChanged(OH_NativeXComponent *component, void *window) {
    LOGE("[Zcy log] OnSurfaceChanged");
    int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
    if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        LOGE("Zcy EGLCore::GLContextInit w = %{public}llu, h = %{public}llu.", width_, height_);
    }
}

// 销毁完试后
void PluginRender::OnSurfaceDestroyed(OH_NativeXComponent *component, void *window) {
    LOGE("[Zcy log] OnSurfaceDestroyed");
}

// 分发完触摸事件后
void PluginRender::DispatchTouchEvent(OH_NativeXComponent *component, void *window) {
    int32_t ret1 = OH_NativeXComponent_GetXComponentOffset(component, window, &x_, &y_);
    int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchInfo_);
    if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        LOGE("[Zcy log] x = %{public}f, y = %{public}f, force=%{public}f", touchInfo_.x, touchInfo_.y, touchInfo_.force);
        for (int i = 0; i < 3; ++i) {
            LOGE("[Zcy log] zbclog touchdots[%{public}d] x=%{public}f y=%{public}f", i, touchInfo_.touchPoints[i].x, touchInfo_.touchPoints[i].y);
        }
    } else {
        LOGE("[Zcy log] DispatchTouchEvent fail");
    }
}

napi_value PluginRender::Export(napi_env env, napi_value exports) {
    // Register JS API
    napi_property_descriptor desc[] = {
        {"drawCube", nullptr, PluginRender::NapiDrawCube, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setTexture", nullptr, PluginRender::NapiDrawCube, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}

napi_value PluginRender::NapiDrawCube(napi_env env, napi_callback_info info) {
    napi_value exportInstance;
    napi_value thisArg;
    napi_status status;
    OH_NativeXComponent *nativeXComponent = nullptr;

    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    
    napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL);

    status = napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
    if (status != napi_ok) {
        return nullptr;
    };

    status = napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent));
    if (status != napi_ok) {
        return nullptr;
    }

    ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return nullptr;
    }

    std::string id(idStr);
    PluginRender *instance = PluginRender::GetInstance(id);
    if (instance) {

        // 多线程
        LOGI("zbclog new pthread");
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
        void *status;
        struct thread_data td;
        td.w = instance->width_;
        td.h = instance->height_;
        td.id = idStr;
        td.eglCore = instance->eglCore_;
        LOGI("zbclog w %{public}d h %{public}d", td.w, td.h);
        int pret = pthread_create(&instance->tid_, NULL, &EGLCore::drawT, (void *)&td);
        if (pret != 0) {
            LOGI("zbclog pthread fail");
        }
        LOGI("zbclog new pthread end");
        pthread_attr_destroy(&attr);
        int rc = pthread_detach(instance->tid_);
        if (rc) {
            LOGI("Error:unable to join %{public}d", rc);
        }
    }
    return nullptr;
}

// 创建JS数组值
napi_value CreateJsArrayValue(napi_env env, std::unique_ptr<uint8_t[]> &data, long length) {
    napi_value buffer;
    napi_status status = napi_create_external_arraybuffer(
        env, data.get(), length,
        [](napi_env env, void *data, void *hint) {
            delete[] static_cast<char *>(data);
        },
        nullptr, &buffer);
    if (status != napi_ok) {
        LOGE("Failed to create external array buffer");
        return nullptr;
    }
    napi_value result = nullptr;
    status = napi_create_typedarray(env, napi_uint8_array, length, buffer, 0, &result);
    if (status != napi_ok) {
        LOGE("Failed to create media typed array");
        return nullptr;
    }
    data.release();
    return result;
}

// 设置纹理
napi_value PluginRender::setTexture(napi_env env, napi_callback_info info) {
    LOGE("GetFileContent Begin");
    size_t requireArgc = 3;
    size_t argc = 2;
    napi_value argv[2] = {nullptr};
    // 获取参数信息
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    // argv[0]即为函数第一个参数Js资源对象，OH_ResourceManager_InitNativeResourceManager转为Native对象。
    NativeResourceManager *mNativeResMgr = OH_ResourceManager_InitNativeResourceManager(env, argv[0]);
    size_t strSize;
    char strBuf[256];
    napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
    std::string filename(strBuf, strSize);

    // 获取rawfile指针对象
    RawFile *rawFile = OH_ResourceManager_OpenRawFile(mNativeResMgr, filename.c_str());
    if (rawFile != nullptr) {
        LOGE("OH_ResourceManager_OpenRawFile success");
    }
    // 获取rawfile大小并申请内存
    long len = OH_ResourceManager_GetRawFileSize(rawFile);
    std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(len);
    // 读取rawfile
    int res = OH_ResourceManager_ReadRawFile(rawFile, data.get(), len);
    // 关闭打开的指针对象
    OH_ResourceManager_CloseRawFile(rawFile);
    OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
    // 转为js对象
    return CreateJsArrayValue(env, data, len);
}

#ifdef __cplusplus
}
#endif